﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "CmdCreateStraightStructureInstanceBase.h"

#include "ActionDrawStraightElementInput.h"
#include "ActionDrawStraightElementCreator.h"
#include "ActionBase.h"
#include "CmdStatusUtils.h"
#include "StructureFamilyUtils.h"
#include "IModelView.h"
#include "StructureLinearInstanceCreator.h"
#include "IGraphicsNodeGroup.h"
#include "StructureInstancePresetParameterUtils.h"
#include "IDocument.h"
#include "IFamily.h"
#include "IUserTransaction.h"
#include "IInstance.h"
#include "IInstanceType.h"
#include "GbmpPresetParameterUIUtils.h"
#include "IActionInputEventArgs.h"
#include "IPropertyPanelEventArgs.h"
#include "PropertyWidgetUtils.h"
#include "IElementModelShape.h"
#include "IGraphicsElementShape.h"
#include "IUiView.h"
#include "IGraphicsStyleManager.h"
#include "StructureCmdParamIds.h"
#include "CommandParameters.h"
#include "GcmpBuiltInCategoryUniIdentities.h"
#include "GbmpJoinUtils.h"
#include "StructureInstanceInput.h"


#include "UiDocumentViewUtils.h"
#include "GbmpGraphicsNodeUtils.h"
#include "GraphicsNodeUtils.h"

#include "IRegenerator.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

CmdCreateStraightStructureInstanceBase::CmdCreateStraightStructureInstanceBase(const std::wstring& commandId)
    : CommandBase(commandId)
{
}

bool CmdCreateStraightStructureInstanceBase::IsEnabled() const
{
    CMD_ONLY_ENABLE_IN_PROJECT_ENVIRONMENT
}

bool CmdCreateStraightStructureInstanceBase::IsVisible() const
{
    CMD_ONLY_VISIBLE_IN_PROJECT_ENVIRONMENT
}

bool CmdCreateStraightStructureInstanceBase::ResetCommand()
{
    m_familyId = ElementId::InvalidID;
    m_instanceTypeId = ElementId::InvalidID;
    m_pAction = nullptr;

    return true;
}

OwnerPtr<IAction> CmdCreateStraightStructureInstanceBase::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    if (!OnExecuteCommand())
    {
        return nullptr;
    }

    bool bOk = this->ResetCommand();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(bOk, L"ResetCommand失败",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    // 创建内建梁族
    StructureFamilyUiUtils::FamilyCreators defaultFamilyCreators = this->GetDefaultFamilyCreators();
    StructureFamilyUiUtils::CreateFamilies(pDoc, defaultFamilyCreators);

    // 获取新创建实例的族名称、实例类型
    std::wstring familyName;
    std::wstring instanceType;
    {
        // 从命令参数读取
        auto itor = cmdParams.find(ID_CMD_PARAM_FAMILY_NAME);
        if (itor != cmdParams.end())
        {
            familyName = itor->second.AsWString();
        }
        itor = cmdParams.find(ID_CMD_PARAM_INSTANCE_TYPE);
        if (itor != cmdParams.end())
        {
            instanceType = itor->second.AsWString();
        }
    }
    if (familyName.empty() || instanceType.empty())
    {
        StructureInstancePresetParameterUtils::GetPresetFamilyNameAndInstanceType(pDoc, this->GetCategory(), this->GetCategorySecondKey(), familyName, instanceType);
    }
    if (familyName.empty())
    {
        familyName = this->GetDefaultFamilyName();
    }
    const IFamily* pFamily = IFamily::GetFamilyByName(pDoc, familyName);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pFamily, L"pFamily为空",L"GDMPLab",L"2024-03-30");

    if (instanceType.empty())
    {
        bOk = StructureFamilyUtils::GetFirstInstanceType(pFamily, instanceType);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(bOk, L"GetFirstInstanceType失败",L"GDMPLab",L"2024-03-30");
    }
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(!instanceType.empty(), L"instanceType为空",L"GDMPLab",L"2024-03-30");

    const IInstanceType* pInstanceType = pFamily->GetInstanceTypeByName(instanceType);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstanceType, L"pInstanceType为空",L"GDMPLab",L"2024-03-30");

    this->SetFamilyIdAndInstanceTypeId(pFamily->GetElementId(), pInstanceType->GetElementId());

    // 配置Action
    ActionDrawStraightElementInput actionInput;
    actionInput.CommandId = GetCommandId();
    actionInput.DrawLineMode = DrawLineMode::Chained;
    actionInput.ActionInitCallback = ActionDrawStraightElementInput::CreateOnActionInitCallback(&CmdCreateStraightStructureInstanceBase::OnInitAction, this);
    actionInput.ActionDestroyCallback = ActionDrawStraightElementInput::CreateOnActionDestroyCallback(&CmdCreateStraightStructureInstanceBase::OnDestroyAction, this);
    actionInput.PromptMessageCallback = ActionDrawStraightElementInput::CreateDefinePomptMessageCallback(&CmdCreateStraightStructureInstanceBase::OnActionPromptMsg, this);
    actionInput.CursorTypeCallback = ActionDrawStraightElementInput::CreateDefineCursorTypeCallback(&CmdCreateStraightStructureInstanceBase::OnDefineCursorType, this);
    actionInput.EnterGettingStartPointStateCallback = ActionDrawStraightElementInput::CreateOnEnterGettingStartPointStateCallback(&CmdCreateStraightStructureInstanceBase::OnEnterGettingStartPointState, this);
    actionInput.EnterGettingEndPointStateCallback = ActionDrawStraightElementInput::CreateOnEnterGettingEndPointStateCallback(&CmdCreateStraightStructureInstanceBase::OnEnterGettingEndPointState, this);
    actionInput.LineUpdateCallback = ActionDrawStraightElementInput::CreateOnLineUpdateCallback(&CmdCreateStraightStructureInstanceBase::OnLineUpdate, this);
    actionInput.LineFinishedCallback = ActionDrawStraightElementInput::CreateOnLineFinishedCallback(&CmdCreateStraightStructureInstanceBase::OnLineFinished, this);
    actionInput.PropertyPanelEventCallback = ActionDrawStraightElementInput::CreateOnPropertyPanelEventCallback(&CmdCreateStraightStructureInstanceBase::OnPropertyPanelEvent, this);
    // ActionInputDlg 相关的回调函数
    actionInput.InputDialogCallback = ActionDrawStraightElementInput::CreateDefineInputDialogCallback(&CmdCreateStraightStructureInstanceBase::DefineInputDialog, this);
    actionInput.InputEventCallback = ActionDrawStraightElementInput::CreateOnActionInputEventCallback(&CmdCreateStraightStructureInstanceBase::OnActionInputEvent, this);

    OwnerPtr<IAction> opAction = ActionDrawStraightElementCreator::Create(actionInput);

    m_pAction = dynamic_cast<ActionBase*>(opAction.get());
    return TransferOwnership(opAction);
}

void CmdCreateStraightStructureInstanceBase::OnInitAction(IUiView* pCurrentView)
{
    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IUserTransaction> opTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"创建预设参数对象"), true);
    if (nullptr == opTransaction.get())
    {
        return;
    }

    std::vector<Vector3d> pts
    {
        Vector3d::Zero,
        Vector3d(1000, 0, 0)
    };

    OwnerPtr<StructureLinearInstanceInput> instanceInput = GetInstanceInput(pts);
    DBG_WARN_AND_RETURN_VOID_UNLESS(instanceInput, L"GetInstanceInput失败",L"GDMPLab",L"2024-03-30");
    instanceInput->CanBeShared = false;
    instanceInput->Visible = false;
    instanceInput->CreationOption = ElementCreationOptions::Transient;
    instanceInput->PositionType = ElementInputPositionType::Line;

    OwnerPtr<IStructureLinearInstanceCreator> opInstanceCreator = GetInstanceCreator();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opInstanceCreator, L"opInstanceCreator为空",L"GDMPLab",L"2024-03-30");

    ElementId presetElementId = ElementId::InvalidID;
    GbmpLineFamilyInstanceCreator* pLinearFamInstance = dynamic_cast<GbmpLineFamilyInstanceCreator*>(opInstanceCreator.get());
    if (pLinearFamInstance)
        presetElementId = opInstanceCreator->CreateInstance(instanceInput.get(), false);
    else
        presetElementId = opInstanceCreator->CreateInstance(instanceInput.get(), true);

    DBG_WARN_AND_RETURN_VOID_UNLESS(presetElementId.IsValid(), L"presetElementId无效",L"GDMPLab",L"2024-03-30");

    if (pLinearFamInstance)
    {
        IInstance* pPresetInstance = quick_cast<IInstance>(pDoc->GetElement(presetElementId));
        DBG_WARN_AND_RETURN_VOID_UNLESS(pPresetInstance, L"pPresetInstance is nullptr!",L"GDMPLab",L"2024-03-30");
        pPresetInstance->SetExpandSubInstances(false);
    }

    opTransaction->Commit();

    // 更新属性面板
    m_pAction->SetPreSetElementId(presetElementId);
    GbmpPresetParameterUIUtils::UpdatePropertyPanel(presetElementId);
    GbmpPresetParameterUIUtils::EnablePropertyPanel(true);

    // 设置Ribbon上下文面板
    this->SetupRibbonContextualPanel();
}

void CmdCreateStraightStructureInstanceBase::OnDestroyAction()
{
    // 属性面板
    GbmpPresetParameterUIUtils::UpdatePropertyPanel(ElementId::InvalidID);
    GbmpPresetParameterUIUtils::EnablePropertyPanel(true);

    // 预设参数对象由基类销毁

    // Ribbon上下文面板由基类恢复
}

const std::wstring CmdCreateStraightStructureInstanceBase::OnActionPromptMsg(DrawLineStatus drawLineStatus)
{
    std::wstring promptMsg;
    switch (drawLineStatus)
    {
    case DrawLineStatus::Getting_StartPoint:
        promptMsg = GBMP_TR(L"请输入直线的起始点");
        break;
    case DrawLineStatus::Getting_EndPoint:
        promptMsg = GBMP_TR(L"请输入直线的终止点");
        break;
    default:
        break;
    }
    return promptMsg;
}

CursorType CmdCreateStraightStructureInstanceBase::OnDefineCursorType(DrawLineStatus drawLineStatus)
{
    return CursorType::DrawLineCursor;
}

void CmdCreateStraightStructureInstanceBase::OnEnterGettingStartPointState(IUiView* pCurrentView)
{
    GbmpPresetParameterUIUtils::EnablePropertyPanel(true);
}

void CmdCreateStraightStructureInstanceBase::OnEnterGettingEndPointState(IUiView* pCurrentView)
{
    GbmpPresetParameterUIUtils::EnablePropertyPanel(false);
}

OwnerPtr<IGraphicsNodeGroup> CmdCreateStraightStructureInstanceBase::OnLineUpdate(IUiView* pCurrentView, const Vector3d& startPt, const Vector3d& endPt)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pCurrentView, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IUserTransaction> opTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"创建预览对象"), true);
    if (nullptr == opTransaction.get())
    {
        return nullptr;
    }

    std::vector<Vector3d> pts
    {
        startPt,
        endPt
    };

    OwnerPtr<StructureLinearInstanceInput> instanceInput = GetInstanceInput(pts);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(instanceInput, L"GetInstanceInput失败",L"GDMPLab",L"2024-03-30");
    instanceInput->CanBeShared = false;
    instanceInput->PositionType = ElementInputPositionType::Line;

    OwnerPtr<IStructureLinearInstanceCreator> opInstanceCreator = GetInstanceCreator();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opInstanceCreator, L"opInstanceCreator为空",L"GDMPLab",L"2024-03-30");

    ElementId previewElementId = ElementId::InvalidID;
    GbmpLineFamilyInstanceCreator* pLinearFamInstance = dynamic_cast<GbmpLineFamilyInstanceCreator*>(opInstanceCreator.get());
    if (pLinearFamInstance)
        previewElementId = opInstanceCreator->CreateInstance(instanceInput.get(), false);
    else
        previewElementId = opInstanceCreator->CreateInstance(instanceInput.get(), true);

    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(previewElementId.IsValid(), L"previewElementId无效",L"GDMPLab",L"2024-03-30");

    IInstance* pPreviewInstance = quick_cast<IInstance>(pDoc->GetElement(previewElementId));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPreviewInstance, L"pPreviewInstance为空",L"GDMPLab",L"2024-03-30");

    if (pLinearFamInstance)
    {
        pPreviewInstance->SetExpandSubInstances(false);
        IRegenerator* pRegenerator = pDoc->GetRegenerator();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pRegenerator, L"pRegenerator为空",L"GDMPLab",L"2024-03-30");
        pRegenerator->RegenerateElement(previewElementId);
    }

    IElementModelShape* pElementModelShape = pPreviewInstance->GetElementModelShape();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElementModelShape, L"pElementModelShape为空",L"GDMPLab",L"2024-03-30");

    const IGraphicsElementShape* pGraphicsElementShape = pElementModelShape->GetGraphicsElementShape();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGraphicsElementShape, L"pGraphicsElementShape为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IGraphicsElementShape> tempGrep = TransferOwnershipCast<IGraphicsElementShape>(pGraphicsElementShape->Clone());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(tempGrep, L"tempGrep为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IGraphicsNodeGroup> tempGroup = GbmpGraphicsNodeUtils::TransferGRepToGGroup(TransferOwnership(tempGrep));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(tempGroup, L"tempGroup为空",L"GDMPLab",L"2024-03-30");

    tempGroup->SetIsSnappable(false);

    {
        // TODO: GraphicsNode上设置的Style/Material Id在事务撤消时需要清理。
        // 如果 IGraphicsElementShape 在事务中创建且事务被撤消了，那么 IGraphicsElementShape 上的样式或者材质的 ElementId 可能就引用到了一个被销毁的 Element 对象上（族文档中有样式或材质被映射到主文档）
        // 下面的代码是将 IGraphicsElementShape 上的样式和材质的 ElementId 全部清空，并将 IGraphicsElementShape 以“临时对象样式”进行渲染
        GraphicsNodeUtils::CleanGraphicsNodeStyleAndMaterial(pDoc, tempGroup.get());
    }

    const UniIdentity& categoryUid = BuiltInCategoryUniIdentities::BICU_TEMP_STYLE;
    IGraphicsStyleManager* pGraphicsStyleManager = pDoc->GetGraphicsStyleManager();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGraphicsStyleManager, L"pGraphicsStyleManager为空",L"GDMPLab",L"2024-03-30");

    tempGroup->SetGraphicsStyleId(pGraphicsStyleManager->GetGraphicsStyleIdByCategoryUid(categoryUid));

    opTransaction->Rollback();

    return tempGroup;
}

ElementId CmdCreateStraightStructureInstanceBase::OnLineFinished(IUiView* pCurrentView, const Vector3d& startPt, const Vector3d& endPt, const ISnap* pStartSnap, const ISnap* pEndSnap)
{
    DBG_WARN_AND_RETURN_UNLESS(pCurrentView, ElementId::InvalidID, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_UNLESS(pDoc, ElementId::InvalidID, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IUserTransaction> opTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"创建实例对象"), true);
    if (nullptr == opTransaction.get())
    {
        return ElementId::InvalidID;
    }

    std::vector<Vector3d> pts
    {
        startPt,
        endPt
    };

    OwnerPtr<StructureLinearInstanceInput> instanceInput = GetInstanceInput(pts);
    DBG_WARN_AND_RETURN_UNLESS(instanceInput, ElementId::InvalidID, L"GetInstanceInput失败",L"GDMPLab",L"2024-03-30");
    instanceInput->PositionType = ElementInputPositionType::Line;

    OwnerPtr<IStructureLinearInstanceCreator> opInstanceCreator = GetInstanceCreator();
    DBG_WARN_AND_RETURN_UNLESS(opInstanceCreator, ElementId::InvalidID, L"opInstanceCreator为空",L"GDMPLab",L"2024-03-30");

    ElementId elementId = opInstanceCreator->CreateInstance(instanceInput.get(), true);
    DBG_WARN_AND_RETURN_UNLESS(elementId.IsValid(), ElementId::InvalidID, L"elementId无效",L"GDMPLab",L"2024-03-30");

    IInstance * pInstance = quick_cast<IInstance>(pDoc->GetElement(elementId));
    DBG_WARN_AND_RETURN_UNLESS(pInstance, ElementId::InvalidID, L"pInstance is nullptr!",L"GDMPLab",L"2024-03-30");
    pInstance->SetExpandSubInstances(true);

    GbmpJoinUtils::CreateJoinsAndCuts(pDoc, elementId);

    opTransaction->Commit();

    return elementId;
}

void CmdCreateStraightStructureInstanceBase::DefineInputDialog(DefineInputDialogParams& inputItems)
{
}

void CmdCreateStraightStructureInstanceBase::OnActionInputEvent(IActionInputEventArgs* pArgs)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pArgs, L"pArgs为空",L"GDMPLab",L"2024-03-30");
}

void CmdCreateStraightStructureInstanceBase::OnPropertyPanelEvent(IPropertyPanelEventArgs* pArgs)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pArgs, L"pArgs为空",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    // TODO: 接口
    if (PropertyWidgetUtils::IsComponentTypeChangedEvent(pArgs->GetPropertyItemUniqueId()))
    {
        ElementId newInstanceTypeId(StringUtil::ToNumber<Int64>(pArgs->GetNewValue()));
        DBG_WARN_AND_RETURN_VOID_UNLESS(newInstanceTypeId.IsValid(), L"newInstanceTypeId无效",L"GDMPLab",L"2024-03-30");

        ElementId newFamilyId;
        bool bOk = StructureFamilyUtils::GetFamilyIdByTypeId(pDoc, newInstanceTypeId, newFamilyId);
        DBG_WARN_AND_RETURN_VOID_UNLESS(bOk, L"GetFamilyNameByTypeId失败",L"GDMPLab",L"2024-03-30");

        if (m_familyId.HasSameValue(newFamilyId) && m_instanceTypeId.HasSameValue(newInstanceTypeId))
            return;

        this->SetFamilyIdAndInstanceTypeId(newFamilyId, newInstanceTypeId);
    }
}

bool CmdCreateStraightStructureInstanceBase::SetFamilyIdAndInstanceTypeId(ElementId familyId, ElementId instanceTypeId)
{
    if (m_familyId.HasSameValue(familyId) && m_instanceTypeId.HasSameValue(instanceTypeId))
        return true;

    m_familyId = familyId;
    m_instanceTypeId = instanceTypeId;

    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IUserTransaction> opTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"保存当前创建实例的族类型到预设参数"), true);
    if (nullptr == opTransaction.get())
    {
        return false;
    }

    bool bOk = StructureInstancePresetParameterUtils::SetPresetInstanceType(pDoc, this->GetCategory(), this->GetCategorySecondKey(), instanceTypeId);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(bOk, L"SetPresetInstanceType失败",L"GDMPLab",L"2024-03-30");

    opTransaction->Commit();

    return true;
}

